package com.hibernate.hrm.service.department;

import com.hibernate.hrm.dao.DepartmentRepository;
import com.hibernate.hrm.dao.OperateLogRepository;
import com.hibernate.hrm.dao.UserRepository;
import com.hibernate.hrm.entity.Department;
import com.hibernate.hrm.entity.OperateLog;
import com.hibernate.hrm.entity.User;
import com.hibernate.hrm.entity.dto.department.DepartmentAddChildDTO;
import com.hibernate.hrm.entity.dto.department.DepartmentAddDTO;
import com.hibernate.hrm.entity.dto.department.DepartmentDetailDTO;
import com.hibernate.hrm.entity.dto.department.DepartmentEditDTO;
import com.hibernate.hrm.entity.dto.employee.UserDTO;
import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.hibernate.Hibernate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class DepartmentServiceImpl implements DepartmentService {

    private final DepartmentRepository departmentRepository;
    private final UserRepository userRepository;
    private final OperateLogRepository operateLogRepository;
    private final HttpServletRequest request;

    public List<Department> getCompleteDepartmentTree(User currentUser) {
        List<Department> roots = departmentRepository.findByDepartmentParentIDIsNull();

        // 权限过滤（如部门经理只能看自己部门）
        if (currentUser.getRoleId().getRoleCode().equals("ROLE_DEPT_MGR")) {
            roots = roots.stream()
                    .filter(dept -> dept.getDepartmentID().equals(currentUser.getDepartmentID().getDepartmentID()))
                    .collect(Collectors.toList());
        }

        // 递归加载所有子部门
        roots.forEach(this::loadAllSubDepartments);
        return roots;
    }

    private void loadAllSubDepartments(Department department) {
        List<Department> children = departmentRepository.findByDepartmentParentID(department);
        department.setSubDepartments(children);
        children.forEach(this::loadAllSubDepartments);
    }

    @Override
    @Transactional
    public Department toggleDepartmentStatus(Integer departmentId, User operator) throws Exception {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new Exception("部门不存在"));

        // 检查是否是董事会（特殊处理）
        if ("董事会".equals(department.getDepartmentName())) {
            throw new Exception("董事会不能被禁用");
        }

        // 当前是启用状态，尝试禁用时需要检查员工
        if (department.getStatus() && hasActiveEmployeesInDepartmentTree(department)) {
            throw new Exception("该部门或其子部门存在在职员工，无法禁用");
        }

        // 如果是启用操作，检查父部门是否被禁用
        if (!department.getStatus()) {
            Department parent = department.getDepartmentParentID();
            while (parent != null) {
                if (!parent.getStatus()) {
                    throw new Exception("上级部门[" + parent.getDepartmentName() + "]处于禁用状态，无法启用当前部门");
                }
                parent = parent.getDepartmentParentID();
            }
        }

        // 切换状态
        boolean newStatus = !department.getStatus();
        department.setStatus(newStatus);
        department.setUpdateTime(LocalDateTime.now());

        // 如果是禁用操作，递归禁用所有子部门
        if (!newStatus) {
            disableAllSubDepartments(department);
        }

        recordDepartmentStatusChangeLog(operator, department, newStatus);

        return departmentRepository.save(department);
    }

    // 添加递归禁用子部门的方法
    private void disableAllSubDepartments(Department department) {
        if (!Hibernate.isInitialized(department.getSubDepartments())) {
            Hibernate.initialize(department.getSubDepartments());
        }

        for (Department subDept : department.getSubDepartments()) {
            subDept.setStatus(false);
            subDept.setUpdateTime(LocalDateTime.now());
            departmentRepository.save(subDept);
            disableAllSubDepartments(subDept);
        }
    }

    private boolean hasActiveEmployeesInDepartmentTree(Department department) {
        // 检查当前部门
        long count = userRepository.countByDepartmentIDAndStatus(department, true);
        if (count > 0) {
            return true;
        }

        // 检查子部门（需要先初始化子部门集合）
        if (!Hibernate.isInitialized(department.getSubDepartments())) {
            Hibernate.initialize(department.getSubDepartments());
        }

        for (Department subDept : department.getSubDepartments()) {
            if (hasActiveEmployeesInDepartmentTree(subDept)) {
                return true;
            }
        }

        return false;
    }

    private long countActiveEmployeesInDepartmentTree(Department department) {
        long count = userRepository.countByDepartmentIDAndStatus(department, true);

        // 递归检查子部门
        for (Department subDept : department.getSubDepartments()) {
            count += countActiveEmployeesInDepartmentTree(subDept);
        }

        return count;
    }

    private void recordDepartmentStatusChangeLog(User operator, Department department, boolean newStatus) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)%s了部门[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                newStatus ? "启用" : "禁用",
                department.getDepartmentName(),
                department.getDepartmentID()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    @Override
    @Transactional
    public void setDepartmentHead(Integer departmentId, Integer headId, User operator) throws Exception {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new Exception("部门不存在"));

        User newHead = userRepository.findById(headId)
                .orElseThrow(() -> new Exception("用户不存在"));

        // 验证负责人是否属于当前部门或其父部门
        if (!isUserInDepartmentOrParent(newHead, department)) {
            throw new Exception("负责人必须属于当前部门或其父部门");
        }

        department.setDepartmentHeadID(newHead);
        department.setUpdateTime(LocalDateTime.now());
        departmentRepository.save(department);

        recordDepartmentHeadChangeLog(operator, department, newHead);
    }

    @Override
    public List<UserDTO> findEligibleHeadCandidates(Integer departmentId) {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new EntityNotFoundException("部门不存在"));

        Set<UserDTO> candidates = new HashSet<>();
        Set<Integer> processedDeptIds = new HashSet<>();
        Queue<Department> queue = new LinkedList<>();
        queue.add(department);

        while (!queue.isEmpty()) {
            Department current = queue.poll();

            if (processedDeptIds.contains(current.getDepartmentID())) {
                continue;
            }
            processedDeptIds.add(current.getDepartmentID());

            // 获取当前部门所有在职用户
            List<User> departmentUsers = userRepository.findByDepartmentIDAndStatusTrue(current);

            // 转换为DTO并添加到候选列表
            departmentUsers.stream()
                    .map(UserDTO::new)
                    .forEach(candidates::add);

            // 添加父部门到队列
            if (current.getDepartmentParentID() != null) {
                queue.add(current.getDepartmentParentID());
            }
        }

        return new ArrayList<>(candidates);
    }

    private boolean isUserInDepartmentOrParent(User user, Department department) {
        Department current = department;
        while (current != null) {
            if (user.getDepartmentID() != null &&
                    user.getDepartmentID().getDepartmentID().equals(current.getDepartmentID())) {
                return true;
            }
            current = current.getDepartmentParentID();
        }
        return false;
    }

    private void recordDepartmentHeadChangeLog(User operator, Department department, User newHead) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)将部门[%s](ID:%d)的负责人设置为[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                department.getDepartmentName(),
                department.getDepartmentID(),
                newHead.getRealName(),
                newHead.getUserId()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    //部门详情
    @Override
    public DepartmentDetailDTO getDepartmentDetail(Integer departmentId) {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new EntityNotFoundException("部门不存在"));

        // 初始化关联数据
        Hibernate.initialize(department.getSubDepartments());
        if (department.getDepartmentHeadID() != null) {
            Hibernate.initialize(department.getDepartmentHeadID().getJobPositionID());
            if (department.getDepartmentHeadID().getJobPositionID() != null) {
                Hibernate.initialize(department.getDepartmentHeadID().getJobPositionID().getDepartmentID());
            }
        }

        DepartmentDetailDTO dto = new DepartmentDetailDTO(department);
        dto.setEmployeeCount((int) departmentRepository.countActiveEmployeesByDepartment(department));
        return dto;
    }

    //编辑部门
    @Override
    public Department getDepartmentForEdit(Integer departmentId) {
        return departmentRepository.findById(departmentId)
                .orElseThrow(() -> new EntityNotFoundException("部门不存在"));
    }

    @Override
    public List<Department> getAvailableParentDepartments(Integer currentDepartmentId) {
        return departmentRepository.findAvailableParentDepartments(currentDepartmentId);
    }

    @Override
    @Transactional
    public void updateDepartment(Integer departmentId, DepartmentEditDTO editDTO, User operator) {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new EntityNotFoundException("部门不存在"));

        // 更新基本信息
        department.setDepartmentName(editDTO.getDepartmentName());
        department.setDepartmentCode(editDTO.getDepartmentCode());
        department.setDepartmentIntroduction(editDTO.getDepartmentIntroduction());

        // 更新上级部门
        if (editDTO.getDepartmentParentID() != null) {
            Department parent = departmentRepository.findById(editDTO.getDepartmentParentID())
                    .orElseThrow(() -> new EntityNotFoundException("上级部门不存在"));
            department.setDepartmentParentID(parent);
            department.setDepartmentLevel(parent.getDepartmentLevel() + 1);
        } else {
            department.setDepartmentParentID(null);
            department.setDepartmentLevel(1);
        }

        // 更新部门负责人
        if (editDTO.getDepartmentHeadID() != null) {
            User head = userRepository.findById(editDTO.getDepartmentHeadID())
                    .orElseThrow(() -> new EntityNotFoundException("用户不存在"));
            department.setDepartmentHeadID(head);
        } else {
            department.setDepartmentHeadID(null);
        }

        department.setUpdateTime(LocalDateTime.now());
        departmentRepository.save(department);

        // 记录操作日志
        recordDepartmentUpdateLog(operator, department);
    }

    private void recordDepartmentUpdateLog(User operator, Department department) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)更新了部门[%s](ID:%d)的信息",
                operator.getRealName(),
                operator.getUserId(),
                department.getDepartmentName(),
                department.getDepartmentID()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    @Override
    @Transactional
    public void addTopDepartment(DepartmentAddDTO addDTO, User operator) {
        // 检查部门代码是否已存在
        if (departmentRepository.existsByDepartmentCode(addDTO.getDepartmentCode())) {
            throw new IllegalArgumentException("部门代码已存在");
        }

        // 创建新部门
        Department department = new Department();
        department.setDepartmentName(addDTO.getDepartmentName());
        department.setDepartmentCode(addDTO.getDepartmentCode());
        department.setDepartmentIntroduction(addDTO.getDepartmentIntroduction());
        department.setDepartmentParentID(null); // 顶级部门
        department.setDepartmentLevel(1); // 顶级部门级别为1
        department.setStatus(true); // 默认启用
        department.setCreateTime(LocalDateTime.now());

        // 设置部门负责人
        if (addDTO.getDepartmentHeadID() != null) {
            User head = userRepository.findById(addDTO.getDepartmentHeadID())
                    .orElseThrow(() -> new IllegalArgumentException("负责人不存在"));
            department.setDepartmentHeadID(head);
        }

        // 保存部门
        Department savedDepartment = departmentRepository.save(department);

        // 记录操作日志
        recordDepartmentAddLog(operator, savedDepartment);
    }

    private void recordDepartmentAddLog(User operator, Department department) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("create");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)添加了顶级部门[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                department.getDepartmentName(),
                department.getDepartmentID()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    @Override
    public List<UserDTO> findEligibleDepartmentHeads() {
        // 查找ROLE_BOSS和ROLE_HR_MGR角色的用户
        List<User> users = userRepository.findByRoleId_RoleCodeInAndStatusTrue(
                Arrays.asList("ROLE_BOSS", "ROLE_HR_MGR"));

        return users.stream()
                .map(UserDTO::new)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void addChildDepartment(Integer parentId, DepartmentAddChildDTO addDTO, User operator) {
        // 检查部门代码是否已存在
        if (departmentRepository.existsByDepartmentCode(addDTO.getDepartmentCode())) {
            throw new IllegalArgumentException("部门代码已存在");
        }

        // 获取父部门
        Department parentDepartment = departmentRepository.findById(parentId)
                .orElseThrow(() -> new IllegalArgumentException("上级部门不存在"));

        // 创建新部门
        Department department = new Department();
        department.setDepartmentName(addDTO.getDepartmentName());
        department.setDepartmentCode(addDTO.getDepartmentCode());
        department.setDepartmentIntroduction(addDTO.getDepartmentIntroduction());
        department.setDepartmentParentID(parentDepartment);
        department.setDepartmentLevel(parentDepartment.getDepartmentLevel() + 1);
        department.setStatus(true); // 默认启用
        department.setCreateTime(LocalDateTime.now());

        // 设置部门负责人
        if (addDTO.getDepartmentHeadID() != null) {
            User head = userRepository.findById(addDTO.getDepartmentHeadID())
                    .orElseThrow(() -> new IllegalArgumentException("负责人不存在"));
            department.setDepartmentHeadID(head);
        }

        // 保存部门
        Department savedDepartment = departmentRepository.save(department);

        // 记录操作日志
        recordDepartmentAddLog(operator, savedDepartment, parentDepartment);
    }

    private void recordDepartmentAddLog(User operator, Department department, Department parentDepartment) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("create");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)在部门[%s](ID:%d)下添加了子部门[%s](ID:%d)",
                operator.getRealName(),
                operator.getUserId(),
                parentDepartment.getDepartmentName(),
                parentDepartment.getDepartmentID(),
                department.getDepartmentName(),
                department.getDepartmentID()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    //删除部门
    @Override
    @Transactional
    public void deleteDepartment(Integer departmentId, User operator) throws Exception {
        Department department = departmentRepository.findById(departmentId)
                .orElseThrow(() -> new Exception("部门不存在"));

        // 检查部门状态，只能删除禁用状态的部门
        if (department.getStatus()) {
            throw new Exception("只能删除禁用状态的部门");
        }

        // 检查是否有在职员工
        if (hasActiveEmployeesInDepartmentTree(department)) {
            throw new Exception("该部门或其子部门存在在职员工，无法删除");
        }

        // 递归删除所有子部门
        deleteAllSubDepartments(department);

        // 记录操作日志
        recordDepartmentDeleteLog(operator, department);

        // 删除当前部门
        departmentRepository.delete(department);
    }

    private void deleteAllSubDepartments(Department department) {
        if (!Hibernate.isInitialized(department.getSubDepartments())) {
            Hibernate.initialize(department.getSubDepartments());
        }

        for (Department subDept : department.getSubDepartments()) {
            // 递归删除子部门
            deleteAllSubDepartments(subDept);

            // 记录子部门删除日志
            recordDepartmentDeleteLog(null, subDept);

            // 删除子部门
            departmentRepository.delete(subDept);
        }
    }

    private void recordDepartmentDeleteLog(User operator, Department department) {
        OperateLog log = new OperateLog();
        if (operator != null) {
            log.setUserId(operator.getUserId());
            log.setOperateDetail(String.format(
                    "用户[%s](ID:%d)删除了部门[%s](ID:%d)",
                    operator.getRealName(),
                    operator.getUserId(),
                    department.getDepartmentName(),
                    department.getDepartmentID()
            ));
        } else {
            log.setOperateDetail(String.format(
                    "系统自动删除子部门[%s](ID:%d)",
                    department.getDepartmentName(),
                    department.getDepartmentID()
            ));
        }
        log.setOperateType("delete");
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    //获取IP地址
    private String getClientIpAddress() {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}